package com.code2roc.fastface.util;

import javax.annotation.*;
import java.lang.annotation.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class ReflectUtil {
    /**
     * 通过反射,获得定义Class时声明的父类的范型参数的类型. 如public BookManager extends
     * GenricManager<Book>
     *
     * @param clazz The class to introspect
     * @return the first generic declaration, or <code>Object.class</code> if cannot be determined
     */
    public static Class getSuperClassGenricType(Class clazz) {
        return getSuperClassGenricType(clazz, 0);
    }

    /**
     * 通过反射,获得定义Class时声明的父类的范型参数的类型. 如public BookManager extends GenricManager<Book>
     *
     * @param clazz clazz The class to introspect
     * @param index the Index of the generic ddeclaration,start from 0.
     */
    public static Class getSuperClassGenricType(Class clazz, int index) throws IndexOutOfBoundsException {
        Type genType = clazz.getGenericSuperclass();
        if (!(genType instanceof ParameterizedType)) {
            return Object.class;
        }
        Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
        if (index >= params.length || index < 0) {
            return Object.class;
        }
        if (!(params[index] instanceof Class)) {
            return Object.class;
        }
        return (Class) params[index];
    }

    public static boolean checkClassAnnotationPresent(Class<?> classz, Class<?> annotationClazz) {
        boolean flag = false;
        List<Annotation> annotationList = Arrays.asList(classz.getAnnotations());
        if(annotationList.size()>0){
            annotationList = annotationList.stream().filter(annotation-> annotation.annotationType() != Deprecated.class &&
                    annotation.annotationType() != SuppressWarnings.class &&
                    annotation.annotationType() != Override.class &&
                    annotation.annotationType() != PostConstruct.class &&
                    annotation.annotationType() != PreDestroy.class &&
                    annotation.annotationType() != Resource.class &&
                    annotation.annotationType() != Resources.class &&
                    annotation.annotationType() != Generated.class &&
                    annotation.annotationType() != Target.class &&
                    annotation.annotationType() != Retention.class &&
                    annotation.annotationType() != Documented.class &&
                    annotation.annotationType() != Inherited.class).collect(Collectors.toList());
            if(annotationList.size()>0){
                for (Annotation annotation : annotationList) {
                    if (annotation.annotationType() == annotationClazz) {
                        flag =  true;
                        break;
                    } else {
                        flag =  checkClassAnnotationPresent(annotation.annotationType(),annotationClazz);
                    }
                }
            }
        }
        return flag;
    }

    public static boolean checkMethodAnnotationPresent(Method method , Class<?> annotationClazz) {
        boolean flag = false;
        List<Annotation> annotationList = Arrays.asList(method.getAnnotations());
        if(annotationList.size()>0){
            annotationList = annotationList.stream().filter(annotation-> annotation.annotationType() != Deprecated.class &&
                    annotation.annotationType() != SuppressWarnings.class &&
                    annotation.annotationType() != Override.class &&
                    annotation.annotationType() != PostConstruct.class &&
                    annotation.annotationType() != PreDestroy.class &&
                    annotation.annotationType() != Resource.class &&
                    annotation.annotationType() != Resources.class &&
                    annotation.annotationType() != Generated.class &&
                    annotation.annotationType() != Target.class &&
                    annotation.annotationType() != Retention.class &&
                    annotation.annotationType() != Documented.class &&
                    annotation.annotationType() != Inherited.class).collect(Collectors.toList());
            if(annotationList.size()>0){
                for (Annotation annotation : annotationList) {
                    if (annotation.annotationType() == annotationClazz) {
                        flag =  true;
                        break;
                    } else {
                        flag =  checkClassAnnotationPresent(annotation.annotationType(),annotationClazz);
                    }
                }
            }
        }
        return flag;
    }

    public static Annotation getClassAnnotation(Class<?> classz, Class<?> annotationClazz) {
        Annotation result = null;
        List<Annotation> annotationList = Arrays.asList(classz.getAnnotations());
        if(annotationList.size()>0){
            annotationList = annotationList.stream().filter(annotation-> annotation.annotationType() != Deprecated.class &&
                    annotation.annotationType() != SuppressWarnings.class &&
                    annotation.annotationType() != Override.class &&
                    annotation.annotationType() != PostConstruct.class &&
                    annotation.annotationType() != PreDestroy.class &&
                    annotation.annotationType() != Resource.class &&
                    annotation.annotationType() != Resources.class &&
                    annotation.annotationType() != Generated.class &&
                    annotation.annotationType() != Target.class &&
                    annotation.annotationType() != Retention.class &&
                    annotation.annotationType() != Documented.class &&
                    annotation.annotationType() != Inherited.class).collect(Collectors.toList());
            if(annotationList.size()>0){
                for (Annotation annotation : annotationList) {
                    if (annotation.annotationType() == annotationClazz) {
                        result =  annotation;
                        break;
                    } else {
                        result =  getClassAnnotation(annotation.annotationType(),annotationClazz);
                    }
                }
            }
        }
        return result;
    }

    public static Annotation getMethodAnnotation(Method method , Class<?> annotationClazz) {
        Annotation result = null;
        List<Annotation> annotationList = Arrays.asList(method.getAnnotations());
        if(annotationList.size()>0){
            annotationList = annotationList.stream().filter(annotation-> annotation.annotationType() != Deprecated.class &&
                    annotation.annotationType() != SuppressWarnings.class &&
                    annotation.annotationType() != Override.class &&
                    annotation.annotationType() != PostConstruct.class &&
                    annotation.annotationType() != PreDestroy.class &&
                    annotation.annotationType() != Resource.class &&
                    annotation.annotationType() != Resources.class &&
                    annotation.annotationType() != Generated.class &&
                    annotation.annotationType() != Target.class &&
                    annotation.annotationType() != Retention.class &&
                    annotation.annotationType() != Documented.class &&
                    annotation.annotationType() != Inherited.class).collect(Collectors.toList());
            if(annotationList.size()>0){
                for (Annotation annotation : annotationList) {
                    if (annotation.annotationType() == annotationClazz) {
                        result =  annotation;
                        break;
                    } else {
                        result =  getClassAnnotation(annotation.annotationType(),annotationClazz);
                    }
                }
            }
        }
        return result;
    }

    public static List<Field> getFieldList(Class clazz){
        List<Field> allFields = new ArrayList<>();
        // 获取当前对象的所有属性字段
        // clazz.getFields()：获取public修饰的字段
        // clazz.getDeclaredFields()： 获取所有的字段包括private修饰的字段
        allFields.addAll(Arrays.asList(clazz.getDeclaredFields()));

        // 获取所有父类的字段， 父类中的字段需要逐级获取
        Class clazzSuper = clazz.getSuperclass();

        // 如果父类不是object，表明其继承的有其他类。 逐级获取所有父类的字段
        while (clazzSuper != Object.class) {
            allFields.addAll(Arrays.asList(clazzSuper.getDeclaredFields()));
            clazzSuper = clazzSuper.getSuperclass();
        }
        return allFields;
    }
}
